<template>
  <ElDrawer v-model="opened" :title="formType" @close="closeHandler">
    <ElSkeleton v-if="loading" animated :rows="10" />
    <ElForm v-else :model="form" ref="formRef" label-width="100" :rules="rules" :disabled="isView">
      <ElFormItem v-if="!isApp" label="上级菜单" prop="parentId">
        <ElTreeSelect
          ref="parentSelectTree"
          node-key="id"
          v-model="form.parentId"
          :data="treeNodes"
          :render-after-expand="false"
          :props="defaultProps"
          check-strictly />
      </ElFormItem>
      <ElFormItem label="菜单类型" prop="type">
        <ElRadioGroup v-model="form.type" :disabled="!isAdd">
          <ElRadioButton label="1" :disabled="!onlyApp">应用</ElRadioButton>
          <ElRadioButton label="2" :disabled="otherTypeDisable">菜单组</ElRadioButton>
          <ElRadioButton label="3" :disabled="otherTypeDisable">菜单</ElRadioButton>
          <ElRadioButton label="4" :disabled="!onlyButton">按钮</ElRadioButton>
        </ElRadioGroup>
      </ElFormItem>
      <ElFormItem label="菜单名称" prop="title">
        <ElInput placeholder="请输入名称" v-model="form.title" />
      </ElFormItem>
      <ElFormItem label="应用编码" prop="appcode" v-if="isApp">
        <ElInput placeholder="请输入应用编码" v-model="form.appcode" />
      </ElFormItem>
      <ElFormItem label="菜单标识" v-if="isMenu" prop="name">
        <ElInput placeholder="菜单标识，不能重复。示例：UserManager" v-model="form.name" />
      </ElFormItem>
      <ElFormItem label="菜单标识" v-if="isMenuGroup">
        <ElInput placeholder="非必填项，示例：UserManager" v-model="form.name" />
      </ElFormItem>
      <ElFormItem label="图标" v-if="!isButtonOrApp">
        <IconPicker v-model="form.icon" />
      </ElFormItem>
      <ElFormItem label="路由地址" v-if="!isButtonOrApp" prop="path">
        <ElInput placeholder="请输入路由地址, 示例：/iot/device" v-model="form.path" />
      </ElFormItem>
      <ElFormItem label="组件路径" v-if="isMenu || isMenuGroup" prop="component">
        <ElInput placeholder="输入相对路径, 示例：/iot/device/index.vue" v-model="form.component" class="i-prepend">
          <template #prepend>src/views</template>
        </ElInput>
      </ElFormItem>
      <ElFormItem label="重定向地址" v-if="!isButton" prop="redirect">
        <ElInput placeholder="请输入重定向, 示例：/iot/device" v-model="form.redirect" />
      </ElFormItem>
      <ElFormItem label="权限标识" v-if="isButton" prop="permission">
        <ElInput placeholder="请输入权限标识, 示例：user-index" v-model="form.permission" />
      </ElFormItem>
      <ElFormItem label="布局组件" v-if="isMenuGroup || isMenu" prop="layoutName">
        <ElInput placeholder="请输入布局组件名称, 为空时默认为：Layout" v-model="form.layoutName" />
      </ElFormItem>
      <ElFormItem label="排序">
        <ElInputNumber v-model="form.sortOrder" class="mx-4" :min="0" :max="999" controls-position="right" />
      </ElFormItem>
      <ElFormItem label="菜单层级">
        <ElTag v-if="form.parentId">{{ form.level }} 级</ElTag>
      </ElFormItem>
      <template v-if="isMenu">
        <ElFormItem label="页面缓存">
          <ElSwitch v-model="form.isKeepAlive" active-text="开启" active-value="1" inactive-text="关闭" inactive-value="0" />
        </ElFormItem>
        <ElFormItem label="显示地图">
          <ElSwitch v-model="form.showMap" active-text="是" active-value="1" inactive-text="否" inactive-value="0" />
        </ElFormItem>
        <ElFormItem label="是否外链">
          <ElSwitch v-model="form.isLink" active-text="是" active-value="1" inactive-text="否" inactive-value="0" />
        </ElFormItem>
        <ElFormItem label="隐藏菜单">
          <ElSwitch v-model="form.isHide" active-text="是" active-value="1" inactive-text="否" inactive-value="0" />
        </ElFormItem>
        <ElFormItem label="Tab页固定">
          <ElSwitch v-model="form.isAffix" active-text="是" active-value="1" inactive-text="否" inactive-value="0" />
        </ElFormItem>
        <ElFormItem label="默认全屏">
          <ElSwitch v-model="form.isFull" active-text="是" active-value="1" inactive-text="否" inactive-value="0" />
        </ElFormItem>
      </template>
      <!-- <ElSpace fill>
        <ElAlert type="info" show-icon :closable="false">
          <p>禁用后右侧菜单不显示</p>
        </ElAlert>
        <ElFormItem label="菜单状态">
          <ElSwitch v-model="form.sts" active-text="启用" active-value="0" inactive-text="禁用" inactive-value="1" />
        </ElFormItem>
      </ElSpace> -->
    </ElForm>
    <template #footer>
      <ElButton v-if="!isView" type="primary" @click="onSaveClicked">保存</ElButton>
      <ElButton v-if="isView" @click="onCancelClicked">关闭</ElButton>
      <ElButton v-else @click="onCancelClicked">取消</ElButton>
    </template>
  </ElDrawer>
</template>

<script setup lang="ts">
import { reactive, ref, unref, computed, watch } from 'vue'
import IconPicker from '@/components/IconPicker/index.vue'
import type { FormInstance, FormRules } from 'element-plus'
import { ElMessage } from 'element-plus'
import { FormType } from '@/enums'
import { Menu } from '@api/uc/menu/interface'
import { validateCode } from '@/utils/validator'
import {
  getMenu,
  fetchParentTree,
  getSortOrder,
  addMenu,
  checkPermissionExist,
  checkNameExist,
  updateMenu,
  checkAppcodeExist
} from '@api/uc/menu'
import { TreeNode } from '@/api/interface'
const formType = ref<FormType>()
const parentSelectTree = ref()
const formRef = ref<FormInstance>()
const form = ref<Partial<Menu.Form>>({})
const opened = ref(false)
const loading = ref(true)
const menuTree = ref<TreeNode[]>([])
const emit = defineEmits(['success'])
interface FlatMenu {
  level: number
  type: string
  id: string
}

interface MenuParam {
  appcode?: string
  id?: string
  parentId?: string
}

const defaultProps = {
  children: 'children',
  label: 'name',
  value: 'id'
}

// 菜单树扁平化数组
const menus = computed<FlatMenu[] | null>(() => {
  if (menuTree.value && menuTree.value.length) {
    return flatTree(menuTree.value)
  }
  return null
})

// disabled处理后的菜单树数据
const treeNodes = computed<TreeNode[]>(() => {
  const current = form.value.type
  if (!current || current === '4') return menuTree.value
  disabledTreNodes(menuTree.value)
  return menuTree.value
})

// 菜单父节点
const parentMenu = computed<FlatMenu | undefined>(() => {
  const temp = unref(menus)
  if (!temp) {
    return undefined
  }
  const node = temp.find(item => {
    return form.value.parentId === item.id
  })
  return node
})

// 是否查看
const isView = computed(() => {
  return formType.value === FormType.VIEW
})

// 是否新增
const isAdd = computed(() => {
  return formType.value === FormType.ADD
})

// 是否app
const isApp = computed(() => {
  const temp = unref(form)
  return temp.type === '1'
})

// 仅允许app按钮
const onlyApp = computed(() => {
  const temp = unref(form)
  return !temp.parentId
})
// 菜单类型禁用状态
const onlyButton = computed(() => {
  const temp = unref(parentMenu)
  // 上级为菜单，下级必须为函数
  return temp?.type === '3'
})

// 禁用app按钮
const otherTypeDisable = computed(() => {
  return onlyApp.value || onlyButton.value
})

// 是否菜单组
const isMenuGroup = computed(() => {
  const temp = unref(form)
  return temp.type === '2'
})

// 是否菜单
const isMenu = computed(() => {
  const temp = unref(form)
  return temp.type === '3'
})

// 是否按钮
const isButton = computed(() => {
  const temp = unref(form)
  return temp.type === '4'
})

// 是否按钮或者应用
const isButtonOrApp = computed(() => {
  return isApp.value || isButton.value
})

// 是否菜单组
const defaultMenu = computed(() => {
  if (isMenu.value) {
    return { permission: '' }
  }
  let menu: any = {
    path: '',
    name: '',
    redirect: '',
    component: '',
    isKeepAlive: '',
    isHide: '',
    isFull: '',
    isLink: '',
    isAffix: '',
    showMap: ''
  }
  if (isMenuGroup.value) {
    menu.permission = ''
  } else {
    menu.redirect = ''
    menu.icon = ''
  }
  return menu
})

// 父菜单变化修改排序和级别等
watch(parentMenu, menu => {
  if (menu?.type === '3' && !form.value.id) {
    form.value.type = '4'
  }

  if (menu && menu.id && isAdd.value) {
    setSortOrder(menu.id)
    form.value.level = menu.level + 1
  }
})

// 权限校验
const validatePermission = (rule: any, value: any, callback: any) => {
  if (!value) {
    return callback()
  }
  checkPermissionExist(value, form.value.id).then(resp => {
    if (resp.data) {
      callback(new Error('[' + value + ']重复，请更换一个权限名称'))
    } else {
      callback()
    }
  })
}

// 菜单标识校验
const validateName = (rule: any, value: any, callback: any) => {
  if (!value) {
    return callback()
  }
  checkNameExist(value, form.value.id).then(resp => {
    if (resp.data) {
      callback(new Error('[' + value + ']重复，请更换一个菜单标识'))
    } else {
      callback()
    }
  })
}
// 应用编码校验
const validateAppcode = (rule: any, value: any, callback: any) => {
  if (!value) {
    return callback()
  }
  checkAppcodeExist(value, form.value.id).then(resp => {
    if (resp.data) {
      callback(new Error('[' + value + ']重复，请更换一个应用编码'))
    } else {
      callback()
    }
  })
}

const rules = reactive<FormRules>({
  path: [{ max: 128, message: '路由地址最大长度 128', trigger: 'blur' }],
  name: [
    { max: 128, message: '菜单标识最大长度 128', trigger: 'blur' },
    { required: true, message: '菜单标识不能为空', trigger: 'blur' },
    { validator: validateCode, trigger: 'blur' },
    { validator: validateName, trigger: 'blur' }
  ],
  redirect: [{ max: 255, message: '重定向最大长度 255', trigger: 'blur' }],
  component: [{ max: 128, message: '组件路径最大长度 128', trigger: 'blur' }],
  title: [
    { required: true, message: '菜单名称不能为空', trigger: 'blur' },
    { max: 32, message: '菜单名称最大长度 32', trigger: 'blur' }
  ],
  permission: [
    { required: true, message: '权限标识不能为空', trigger: 'blur' },
    { max: 32, message: '组件最大长度 32', trigger: 'blur' },
    { validator: validateCode, trigger: 'blur' },
    { validator: validatePermission, trigger: 'blur' }
  ],
  appcode: [
    { required: true, message: '应用编码不能为空', trigger: 'blur' },
    { max: 20, message: '组件最大长度 20', trigger: 'blur' },
    { validator: validateCode, trigger: 'blur' },
    { validator: validateAppcode, trigger: 'blur' }
  ],
  type: [{ required: true, message: '菜单类型不能为空', trigger: 'blur' }],
  parentId: [{ required: true, message: '上级菜单不能为空', trigger: 'blur' }],
  layoutName: [
    { max: 100, message: '布局组件名最大长度100', trigger: 'blur' },
    { validator: validateCode, trigger: 'blur' }
  ]
})

function setSortOrder(id: string) {
  getSortOrder(id).then(resp => {
    const { data } = resp
    form.value.sortOrder = data ? data + 1 : 1
  })
}

// 初始化菜单树数据
function init() {
  fetchParentTree(form.value.appcode).then(resp => {
    const { data } = resp
    menuTree.value = data
  })
}

// 递归扁平化菜单数据方法
function flatTree(tree: TreeNode[]) {
  let res: FlatMenu[] = []
  for (const item of tree) {
    const { children, ...i } = item
    if (children && children.length) {
      res = res.concat(flatTree(children))
    }
    res.push(treeNode2FlatMenu(i))
  }
  return res
}

// 菜单树节点是否禁用选择
function canNodeChecked(node: TreeNode) {
  const current = form.value.type
  const type = node.data.type
  if (current === '3') {
    return type === '3'
  }
  if (current === '2') {
    return type === '3'
  }
  return false
}

// 菜单树数据禁用状态处理方法
function disabledTreNodes(tree: TreeNode[]) {
  for (const item of tree) {
    const { children } = item
    if (children && children.length) {
      disabledTreNodes(children)
    }
    item.disabled = canNodeChecked(item)
  }
}

// TreeNode节点转扁平化菜单
function treeNode2FlatMenu(node: TreeNode): FlatMenu {
  return {
    id: node.id,
    type: node.data?.type as string,
    level: node.data?.level as number
  }
}

// 打开表单
async function open(type: FormType, param: MenuParam) {
  formType.value = type
  opened.value = true
  if (isAdd.value) {
    loading.value = false
    form.value = { sts: '0', appcode: param.appcode, parentId: param.parentId }
    if (onlyApp.value) {
      form.value.type = '1'
      setSortOrder('-1')
    } else if (onlyButton.value) {
      form.value.type = '4'
    }
  } else {
    try {
      const { data } = await getMenu(param.id!)
      loading.value = false
      form.value = data
    } finally {
      loading.value = false
    }
  }
  init()
  formRef.value?.clearValidate()
}

// 关闭表单
function close() {
  loading.value = true
  opened.value = false
}

// 获取所有父类节点id用于展开
function getAllParent() {
  const arr: string[] = []
  if (!parentSelectTree.value) {
    return arr
  }
  let node = parentSelectTree.value.getNode(form.value.parentId)
  while (node.data && node.data.id) {
    arr.unshift(node.data.id)
    if (node.data.level === 1) break
    node = node.parent
  }
  return arr
}

// 保存按钮
function onSaveClicked() {
  if (!formRef.value) return
  formRef.value?.validate(valid => {
    if (!valid) {
      return
    }
    const menu = Object.assign({}, unref(form), unref(defaultMenu))
    if (isAdd.value) {
      addMenu(menu)
        .then(() => {
          success()
          sendSuccess()
          close()
        })
        .catch(error)
    } else {
      updateMenu(menu).then(() => {
        success()
        sendSuccess()
        close()
      })
    }
  })
}

// drawer关闭前事件处理
function closeHandler() {
  menuTree.value = []
}

// 成功弹出提示
function success() {
  ElMessage({
    type: 'success',
    message: `成功!`
  })
}

// 成功失败弹出提示
function error(err: string) {
  ElMessage({
    type: 'error',
    message: `失败!  ${err}`
  })
}

// 取消按钮
function onCancelClicked() {
  close()
}

// 保存成功发送成功事件
function sendSuccess() {
  emit('success', getAllParent())
}

defineExpose({
  open,
  close
})
</script>
<style scoped lang="scss">
:deep(.el-input-group__prepend) {
  padding: 0 10px;
}
</style>
